﻿/** \file		reg_pool.h
 *	\date		(2010-03-10 23:49:54)/(2014-11-29 16:08:46)
 *-----------------------------------------------------------------------------
 *	\version	1.0.10.13
 *	\author		Nick Shallery	(nicknide@gmail.com)
 *	\copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/


#if			!defined(__LOLIX__LXUT__REG_POOL___H__K_IN_G__)
#define		__LOLIX__LXUT__REG_POOL___H__K_IN_G__

#if			!defined(__LOLIX__LOLIX_INSTANCE__K_IN_G__)
	#include	"../lolix/lolix_inst.h"
#endif	//	!defined(__LOLIX__LOLIX_INSTANCE__K_IN_G__)

#if			!defined(__LOLIX__LOLI__FILE__H__K_IN_G__)
	#include	"../lolix/loli/file.h"
#endif	//	!defined(__LOLIX__LOLI__FILE__H__K_IN_G__)

#if			!defined(__LOLIX__TOY__INVOKE_CAST__HPP__K_IN_G__)
	#include	"../lolix/toy/invoke_cast.hpp"
#endif	//	!defined(__LOLIX__TOY__INVOKE_CAST__HPP__K_IN_G__)

namespace lolix{namespace toy{
	class stdc_fun;
}}

namespace lxut{

class LOLIX_NO_VTABLE reg_pool
	: public lolix::itf
{
public:
	LOLIX_DECLARE_ITF(lxut::reg_pool, lolix::itf, (0x71aa8e33, 0x0c6c4fb5, 0x96c4d2a0, 0x28799a54));

	enum ENUM_KEY_TYPE
	{	KEY_TYPE_NULL
	,	KEY_TYPE_BINARY
	,	KEY_TYPE_UINT
	,	KEY_TYPE_WSTRING
	,	KEY_TYPE_BOOLEAN
	,	KEY_TYPE_ITF_TYPE_ID
	,	KEY_TYPE_F32
	,	_KEY_TYPE_CNT
	,	_KEY_TYPE_VER = 1
	};

	typedef lolix::toy::invoker_process<lolix::LOLIX_RETNV, false, lolix::toy::type_list<const wchar_t*, size_type, void*> > _T_Enum_obj;
	LOLIX_INTERFACE(getname		, size_type			, (wchar_t* name, size_type name_buf)const);
	LOLIX_INTERFACE(rename		, lolix::LOLIX_RETNV, (const wchar_t* name, size_type name_size));
	LOLIX_INTERFACE(find_key	, lolix::LOLIX_RETNV, (const reg_pool**, const wchar_t* name, size_type name_size)const);
	LOLIX_INTERFACE(query_key	, lolix::LOLIX_RETNV, (reg_pool**, const wchar_t* name, size_type name_size, bool create_if_no_exist));
	LOLIX_INTERFACE(remove_key	, lolix::LOLIX_RETNV, (const wchar_t* name, size_type name_size));
	//===========================================================================
	//	将一个存在的 rpool 以指定的名字插入其他的 rpool 子节点下
	//---------------------------------------------------------------------------
	//	rp			[i ] :	需要插入的对象
	//---------------------------------------------------------------------------
	//	lolix::LOLIX_ERROR_RES_ALREADY_EXIST : 此节点已经存在
	//---------------------------------------------------------------------------
	//	不支持递归查找
	//===========================================================================
	LOLIX_INTERFACE(insert_key	, lolix::LOLIX_RETNV, (reg_pool* rp));
	LOLIX_INTERFACE(read_val	, lolix::LOLIX_RETNV, (void* ptr, ENUM_KEY_TYPE* key_type, size_type* dat_size)const);
	LOLIX_INTERFACE(write_val	, lolix::LOLIX_RETNV, (const void* ptr, ENUM_KEY_TYPE key_type, size_type dat_size));
	LOLIX_INTERFACE(size		, lolix::LOLIX_RETNV, (size_type* sub_items_count)const);
	LOLIX_INTERFACE(enum_items	, lolix::LOLIX_RETNV, (_T_Enum_obj* enum_obj, void* usr_dat)const);
	LOLIX_INTERFACE(write		, lolix::LOLIX_RETNV, (lolix::loli::file_flow*)const);
	LOLIX_INTERFACE(read		, lolix::LOLIX_RETNV, (lolix::loli::file_flow*));

	#pragma region	inline call
	template<typename _Proc>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV enum_items(_Proc& enum_obj)
	{
		class _TmpItem
		{
		public:
			LOLIX_INLINE_CALL lolix::LOLIX_RETNV LOLIX_CALL invoke(const lolix::lx_wchar* name, size_type nameLen, void* usrptr)
			{
				return (*static_cast<_Proc*>(usrptr))(name, nameLen);
			}
		}tmpItem;
		_T_Enum_obj eobj(&tmpItem, &_TmpItem::invoke);
		return this->enum_items(&eobj, &enum_obj);
	}

	template<size_type name_len>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV find_key(const reg_pool** out_ptr, const wchar_t (&name)[name_len])const
	{
		return this->find_key(out_ptr, name, name_len - 1);
	}

	LOLIX_INLINE_CALL lolix::LOLIX_RETNV read_val(void* ptr, ENUM_KEY_TYPE* key_type, size_type buf_size)const
	{
		lolix::LOLIX_RETNV rtv;
		lolix::size_type dat_size = 0;
		if ( LOLIX_IS_ERROR(rtv = this->read_val(0, 0, &dat_size)) )
			return rtv;
		if ( buf_size < dat_size )
			return lolix::LOLIX_ERROR_LESS_MEMORY;
		return this->read_val(ptr, key_type, &dat_size);
	}

	LOLIX_INLINE_CALL lolix::LOLIX_RETNV read_cval(void* ptr, ENUM_KEY_TYPE* key_type, size_type* dat_size, const wchar_t* name, size_type name_size)const
	{
		lolix::LOLIX_RETNV rtv;
		const reg_pool* chl;
		if ( LOLIX_IS_ERROR(rtv = this->find_key(&chl, name, name_size)) )
			return rtv;
		rtv = chl->read_val(ptr, key_type, dat_size);
		chl->release();
		return rtv;
	}

	LOLIX_INLINE_CALL lolix::LOLIX_RETNV read_cval(void* ptr, ENUM_KEY_TYPE* key_type, size_type dat_size, const wchar_t* name, size_type name_size)const
	{
		lolix::LOLIX_RETNV rtv;
		const reg_pool* chl;
		if ( LOLIX_IS_ERROR(rtv = this->find_key(&chl, name, name_size)) )
			return rtv;
		rtv = chl->read_val(ptr, key_type, dat_size);
		chl->release();
		return rtv;
	}

	LOLIX_INLINE_CALL lolix::LOLIX_RETNV write_cval(const void* ptr, ENUM_KEY_TYPE key_type, size_type dat_size, const wchar_t* name, size_type name_size)
	{
		lolix::LOLIX_RETNV rtv;
		reg_pool* chl;
		if ( LOLIX_IS_ERROR(rtv = this->query_key(&chl, name, name_size, true)) )
			return rtv;
		rtv = chl->write_val(ptr, key_type, dat_size);
		chl->release();
		return rtv;
	}

	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_val(lolix::lx_u32* val)const
	{
		lolix::lx_u64 udat;
		lolix::size_type dat_size = sizeof udat;
		ENUM_KEY_TYPE ekey;
		lolix::LOLIX_RETNV rtv;
		if ( LOLIX_IS_ERROR(rtv = this->read_val(&udat, &ekey, dat_size)) )
			return rtv;
		if ( ekey != KEY_TYPE_UINT )
			return lolix::LOLIX_ERROR_INVALID_TYPE;
		if ( udat > 0xffffffff )
			return lolix::LOLIX_ERROR_OUT_OF_RANGE;
		*val = (lolix::lx_u32)udat;
		return lolix::LOLIX_RETNV_OK;
	}

	template<size_type _Len>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_uint(lolix::lx_u32* val, const wchar_t (&name)[_Len])const
	{
		return this->get_uint(val, name, _Len-1);
	}
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_uint(lolix::lx_u32* val, const wchar_t* name, size_type name_size)const
	{
		lolix::lx_u64 udat;
		lolix::size_type dat_size = sizeof udat;
		ENUM_KEY_TYPE ekey;
		lolix::LOLIX_RETNV rtv;
		if ( LOLIX_IS_ERROR(rtv = this->read_cval(&udat, &ekey, dat_size, name, name_size)) )
			return rtv;
		if ( ekey != KEY_TYPE_UINT )
			return lolix::LOLIX_ERROR_INVALID_TYPE;
		if ( udat > 0xffffffff )
			return lolix::LOLIX_ERROR_OUT_OF_RANGE;
		*val = (lolix::lx_u32)udat;
		return lolix::LOLIX_RETNV_OK;
	}

	template<size_type buf_size>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_wchar(wchar_t (&dat)[buf_size], const wchar_t* name, size_type name_size)const
		{
		return get_wchar(dat, buf_size, name, name_size);
		}
	template<size_type buf_size, size_type name_size>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_wchar(wchar_t (&dat)[buf_size], const wchar_t (&name)[name_size])const
		{
		return get_wchar(dat, buf_size, name, name_size - 1);
		}

	template<size_type buf_size>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_wchar(wchar_t (&dat)[buf_size], size_type* str_len, const wchar_t* name, size_type name_size)const
		{
		return get_wchar(dat, buf_size, str_len, name, name_size);
		}
	template<size_type buf_size, size_type name_size>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_wchar(wchar_t (&dat)[buf_size], size_type* str_len, const wchar_t (&name)[name_size])const
		{
		return get_wchar(dat, buf_size, str_len, name, name_size - 1);
		}

	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_wchar(wchar_t* dat, size_type buf_size, const wchar_t* name, size_type name_size)const
		{
		size_type out_len;
		lolix::LOLIX_RETNV rtv;
		if ( !LOLIX_IS_ERROR(rtv = get_wchar(dat, buf_size, &out_len, name, name_size)) )
			dat[out_len] = 0;
		return rtv;
		}

	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_wchar(wchar_t* dat, size_type buf_size, size_type* str_len, const wchar_t* name, size_type name_size)const
		{
		lolix::LOLIX_RETNV rtv;
		const lolix::size_type dat_size = buf_size * sizeof dat[0];
		lolix::size_type true_size = dat_size;
		ENUM_KEY_TYPE ekey;
		if ( LOLIX_IS_ERROR(rtv = this->read_cval(dat, &ekey, &true_size, name, name_size)) )
			return rtv;
		if ( ekey != KEY_TYPE_WSTRING )
			return lolix::LOLIX_ERROR_INVALID_TYPE;

		if ( true_size > dat_size )
			return lolix::LOLIX_ERROR_LESS_MEMORY;

		true_size /= 2;
		*str_len = true_size;
		return lolix::LOLIX_RETNV_OK;
		}
	template<typename _Type>
	class _type_to_enum;
	template<typename _Ty>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_val(_Ty* val)const
	{
		_Ty udat;
		lolix::size_type dat_size = sizeof(udat);
		ENUM_KEY_TYPE ekey;
		lolix::LOLIX_RETNV rtv;
		if ( LOLIX_IS_ERROR(rtv = this->read_val(&udat, &ekey, dat_size)) )
			return rtv;
		if ( ekey != _type_to_enum<_Ty>::eval )
			return lolix::LOLIX_ERROR_INVALID_TYPE;
		*val = udat;
		return lolix::LOLIX_RETNV_OK;
	}

	template<typename _Ty>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV set_val(_Ty const& val)
	{
		return this->write_val(&val, (ENUM_KEY_TYPE)_type_to_enum<_Ty>::eval, sizeof(val));
	}

	template<typename _Ty>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_val(_Ty* val, const wchar_t* name, size_type name_size)const
	{
		_Ty udat;
		lolix::size_type dat_size = sizeof(udat);
		ENUM_KEY_TYPE ekey;
		lolix::LOLIX_RETNV rtv;
		if ( LOLIX_IS_ERROR(rtv = this->read_cval(&udat, &ekey, dat_size, name, name_size)) )
			return rtv;
		if ( ekey != _type_to_enum<_Ty>::eval )
			return lolix::LOLIX_ERROR_INVALID_TYPE;
		*val = udat;
		return lolix::LOLIX_RETNV_OK;
	}

	template<typename _Ty>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV set_val(const _Ty& val, const wchar_t* name, size_type name_size)
	{
		return this->write_cval(&val, (ENUM_KEY_TYPE)_type_to_enum<_Ty>::eval, sizeof(val), name, name_size);
	}

	template<typename _Ty, size_type name_len>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV get_val(_Ty* val, const wchar_t (&name)[name_len])const
	{ return this->get_val(val, name, name_len - 1); }
	template<typename _Ty, size_type name_len>
	LOLIX_INLINE_CALL lolix::LOLIX_RETNV set_val(_Ty const& val, const wchar_t (&name)[name_len])
	{ return this->set_val(val, name, name_len - 1); }

	#pragma endregion
};


class LOLIX_NO_VTABLE reg_pool_factory
	: public ::lolix::lolix_factory
{
	LOLIX_DECLARE_ITF(lxut::reg_pool_factory, lolix::lolix_factory, (0x4fc23e6f, 0x03384668, 0xb5ec5896, 0x8c95941a));
public:
	LOLIX_INTERFACE(create_inst, LOLIX_RETNV, (reg_pool** out_ptr));
};

#define		__LOLIX_TMP__TYPE_T_ENUM(tp, en)	\
template<> class reg_pool::_type_to_enum<tp>{public: enum {eval = reg_pool::en};}

__LOLIX_TMP__TYPE_T_ENUM(bool, KEY_TYPE_BOOLEAN);
__LOLIX_TMP__TYPE_T_ENUM(float, KEY_TYPE_F32);
__LOLIX_TMP__TYPE_T_ENUM(lolix::LOLIX_ITF_ID, KEY_TYPE_ITF_TYPE_ID);
__LOLIX_TMP__TYPE_T_ENUM(lolix::lx_u32, KEY_TYPE_UINT);
__LOLIX_TMP__TYPE_T_ENUM(lolix::lx_u64, KEY_TYPE_UINT);
#undef		__LOLIX_TMP__TYPE_T_ENUM

}


#endif	//	!defined(__LOLIX__LXUT__REG_POOL___H__K_IN_G__)
